package ch.nostromo.tiffanys.game.pgn;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringReader;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.StringTokenizer;
import java.util.Vector;

import uk.ac.cam.cl.projectcharlie2010.game.GameController;
import uk.ac.cam.cl.projectcharlie2010.game.player.PlayerHuman;

import ch.nostromo.lib.util.NosTools;
import ch.nostromo.tiffanys.game.GameControllerException;
import ch.nostromo.tiffanys.game.game.Game;
import ch.nostromo.tiffanys.game.game.GameException;
import ch.nostromo.tiffanys.game.game.GameState;
import ch.nostromo.tiffanys.game.game.GameTools;

public class PGN {

  private static int STATE_HEADER = 1;

  private static int STATE_MOVES = 2;

  public static GameController importFirstPGN(File pgnFile) throws GameControllerException {
    return importPGN(pgnFile)[0];
  }

  public static GameController importFirstPGN(String pgn) throws GameControllerException {
    return importPGN(pgn)[0];
  }

  public static GameController[] importPGN(String pgn) throws GameControllerException {
    BufferedReader reader = new BufferedReader(new StringReader(pgn));

    return importPGN(reader, 0);
  }

  public static GameController[] importPGN(File pgnFile) throws GameControllerException {
    try {
      BufferedReader reader = new BufferedReader(new FileReader(pgnFile));

      return importPGN(reader, 0);
    } catch (FileNotFoundException e) {
      throw new PGNException("File not found: " + pgnFile, e);
    }
  }

  public static GameController[] importPGN(File pgnFile, int limitMoves) throws GameControllerException {
    try {
      BufferedReader reader = new BufferedReader(new FileReader(pgnFile));

      return importPGN(reader, limitMoves);
    } catch (FileNotFoundException e) {
      throw new PGNException("File not found: " + pgnFile, e);
    }
  }

  private static GameController[] importPGN(BufferedReader reader, int limitMoves) throws GameControllerException {

    Vector<GameController> result = new Vector<GameController>();

    String line;
    int state = STATE_HEADER;

    GameController currentGameController = null;

    boolean newLineRead = false;
    
    try {
      while ((line = reader.readLine()) != null) {

        if (line.trim().length() > 0 && line.charAt(0) == '[') {
          newLineRead = true;
          // create new game if not done yet
          if (currentGameController == null) {
            currentGameController = new GameController();
          }

          StringTokenizer st = new StringTokenizer(line, " []");
          String tagName = st.nextToken();
          String tagValue = st.nextToken("]");
          tagValue = tagValue.substring(2, tagValue.length() - 1);

          // System.err.println( "Tag: " + tagName + ", Value: " + tagValue ) ;
          if (tagName.equals("Event")) {
            currentGameController.setEventName(tagValue);
          } else if (tagName.equals("Site")) {
            currentGameController.setEventSite(tagValue);
          } else if (tagName.equals("Date")) {
            currentGameController.setEventDate(tagValue);
          } else if (tagName.equals("Round")) {
            currentGameController.setEventRound(tagValue);
          } else if (tagName.equals("White")) {
            currentGameController.setPlayerWhite(new PlayerHuman(tagValue));
          } else if (tagName.equals("Black")) {
            currentGameController.setPlayerBlack(new PlayerHuman(tagValue));
          }

          state = STATE_HEADER;
          continue;
        }

        if (line.trim().equals("") && newLineRead) {
          newLineRead = false;
          if (state == STATE_HEADER) {
            state = STATE_MOVES;
          } else {
            // Save last game if != null & we've been readinng moves
            if (currentGameController != null && state == STATE_MOVES) {
              result.add(currentGameController);
              currentGameController = null;
              
            }
            state = STATE_HEADER;
          }
          continue;
        }

        StringTokenizer st = new StringTokenizer(line, " ");
        while (st.hasMoreTokens()) {
          
          newLineRead = true;
          
          String move = st.nextToken();

          if (move.startsWith("*") || move.startsWith("1-0") || move.startsWith("0-1") || move.startsWith("1/2 - 1/2")|| move.startsWith("1/2-1/2") ) {
            continue;
          }

          if (limitMoves > 0 && currentGameController.getGame().getMoveCount() >= limitMoves) {
            // cut
           
          } else {
          
            int point = move.indexOf(".");
            if (point >= 0) {
              move = move.substring(point + 1);
            }

            if (currentGameController != null) {
              currentGameController.enterNextMove(move);
            }

          }

        }

      }
    } catch (IOException e) {
      throw new PGNException("IO Error", e);
    }

    if (currentGameController != null && state == STATE_MOVES) {
      result.add(currentGameController);
    }

    return result.toArray(new GameController[result.size()]);

  }

  public static void exportPGN(GameController gameController, File file) throws GameControllerException {
    try {
      String pgn = exportPGN(gameController);
      NosTools.writeTextFile(file, pgn);
    } catch (GameControllerException e) {
      throw e;
    } catch (IOException e) {
      throw new PGNException("IO Exception", e);
    }
  }

  public static String exportPGNMovesLine(Game game, int limitCount) {
    int tmpColor = game.getInitialColorToMove();
    int moveCounter = 1;
    String currentLine = "";
    int movesCount = game.getHistoryMoves().size();
    if (movesCount > limitCount && limitCount > 0) {
      movesCount = limitCount;
    }
    for (int i = 0; i < movesCount; i++) {

      String addOn = "";
      if (tmpColor == Game.COLOR_WHITE) {
        addOn += moveCounter + ".";
        moveCounter++;
      }

      addOn += game.getHistoryMoves().get(i).getSanDescription();
      addOn += " ";
      currentLine += addOn;

      try {
        tmpColor = GameTools.switchColor(tmpColor);
      } catch (GameException e) {
        // ignore
      }
    }

    if (currentLine.length() > 0) {
      currentLine = currentLine.substring(0, currentLine.length() -1);
    }
    
    return currentLine;
    
  }
  
  public static String exportPGN(GameController gameController) throws GameControllerException {

    StringBuffer result = new StringBuffer();
    result.append("[Site \"?\"]\n");

    if (gameController.getEventDate() == null || gameController.getEventDate().equals("")) {
      SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd");
      String date = sdf.format(new Date());
      result.append("[Date \"" + date + "\"]\n");
    } else {
      result.append("[Date \"" + gameController.getEventDate() + "\"]\n");
    }
    result.append("[Round \"-\"]\n");

    result.append("[White \"" + gameController.getPlayerWhite().getPlayerName() + "\"]\n");
    result.append("[Black \"" + gameController.getPlayerBlack().getPlayerName() + "\"]\n");

    result.append("[Result \"" + getPGNState(gameController.getCurrentGameState()) + "\"]\n");

    result.append("\n");

    int tmpColor = gameController.getGame().getInitialColorToMove();
    int moveCounter = 1;
    String currentLine = "";
    int movesCount = gameController.getGame().getHistoryMoves().size();
    for (int i = 0; i < movesCount; i++) {

      String addOn = "";
      if (tmpColor == Game.COLOR_WHITE) {
        addOn += moveCounter + ".";
        moveCounter++;
      }

      addOn += gameController.getGame().getHistoryMoves().get(i).getSanDescription();
      addOn += " ";

      if (currentLine.length() + addOn.length() > 78) {
        result.append(currentLine + "\n");
        currentLine = addOn;
      } else {
        currentLine += addOn;
      }

      tmpColor = GameTools.switchColor(tmpColor);
    }

    result.append(currentLine);
    result.append(getPGNState(gameController.getCurrentGameState()));

    return result.toString();
  }

  private static String getPGNState(GameState gs) {
    if (gs.gameOpen()) {
      return "*";
    } else if (gs.blackMates()) {
      return "0-1";
    } else if (gs.whiteMates()) {
      return "1-0";
    } else {
      return "1/2-1/2";
    }
  }

  public static String exportFEN(Game game) {
    String result = "";
    result += game.getCurrentBoard().getFENPosition();
    result += " ";

    if (game.getCurrentColorToMove() == Game.COLOR_WHITE) {
      result += "w";
    } else {
      result += "b";
    }
    result += " ";

    result += game.getCurrentBoard().getFENCastling();
    result += " ";

    result += game.getCurrentBoard().getFENEnPassant();
    result += " ";

    result += game.getHistoryHitMoveOffset();
    result += " ";

    result += game.getMoveCount() / 2;

    return result;
  }

}
